package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.message.SignlnMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

@Service
@Slf4j
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper rabbitMqHelper;


    @Override/*签到接口*/
    public SignResultVO addSignRecord() {
        //1.查询用户是否已经签到
        Long userId = UserContext.getUser();
        //// 1.2.获取日期
        LocalDate now = LocalDate.now();
        //3拼接key
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX
                + userId
                + now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        //4. 1.4.计算offset
        int offset = now.getDayOfMonth() - 1;

        //5.保存你签到信息（true代表1签到，false代表未签到）
        Boolean exists = redisTemplate.opsForValue().setBit(key, offset, true);
        if (BooleanUtils.isTrue(exists)) {
            throw new RuntimeException("已经签过到了！");
        }

        //计算连续签到的天数
        int signDays = countSignDays(key, now.getDayOfMonth());/*可以理解为查询多少天*/

        //计算签到的得分(按天数算分)
        int rewardPoints = 0;
        switch (signDays) {
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
            default:
                rewardPoints = 0;
                break;
        }


        //保存积分明细记录 ,需要发送消息，接受消息后存入数据库
        rabbitMqHelper.send(
                MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignlnMessage.of(userId, rewardPoints + 1));// 签到积分是基本得分+奖励积分


        //封装vo
        SignResultVO vo = new SignResultVO();
        vo.setSignPoints(rewardPoints);
        vo.setSignDays(signDays);

        return vo;
    }

    /*=====================================查询签到记录=====================================================================*/
    @Override
    public Byte[] querySignRecords() {
        // 1、获取key
// 1.1、获取当前登录用户
        Long userId = UserContext.getUser();

// 1.2、获取当前日期
        LocalDate now = LocalDate.now();
        String dateStr = now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);

// 1.3、拼接key
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId + dateStr;

// 2、查询截止到当天的签到记录
        int dayOfMonth = now.getDayOfMonth();
        List<Long> result = redisTemplate.opsForValue()
                .bitField(key,
                        BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
                );
        if (ObjectUtil.isEmpty(result)) {
            return new Byte[]{};
        }
        int signResult = result.get(0).intValue();

// 3、循环每一天的签到情况，赋值给byte数组
        Byte[] arr = new Byte[dayOfMonth];

        int pos = dayOfMonth - 1;
        while (pos >= 0) {
            arr[pos--] = (byte) (signResult & 1);
            signResult >>>= 1;
        }

        return arr;
    }


    /*==================================================================================================*/
    private int countSignDays(String key, int days) {/*计算连续签到的天数*/
        //从第一天开始遍历到今天，所有的签到记录
        List<Long> allSignList = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(
                BitFieldSubCommands.BitFieldType.unsigned(days)).valueAt(0));/*从第一天开始*/

        //遍历
        if (allSignList == null || allSignList.isEmpty()) {
            return 0;
        }
        /*因为拿到哦的是集合，集合中只有一条数据，拿出数据做&运算*/
        int num = allSignList.get(0).intValue();
        //弄个计时器用来记录连续多少天
        int count = 0;

        /*循环，与1做与运算，得到最后一个bit，判断是否为0，为0则终止，为1则继续*/
        while ((num & 1) == 1) {
            count++;
            num >>>= 1;/*把数字右移一位，最后一位被舍弃，倒数第二位成了最后一位*/
        }
        return count;
    }


}
